/*
 * Copyright (C) 2010 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.action;

import java.lang.ref.*;
import java.util.*;

/**
 * Helper holding an selection but without any strong reference.<br>
 * Use this class to store selections without the risk of memory leaks.
 * 
 * @param <T> The selection content type
 * @author M. Hautle
 */
public class SelectionHolder<T> {
    /** A reference on the list itself. (just for performance reasons) */
    private Reference<List<T>> list;

    /** List holding references to each entry of {@link #list}. */
    private List<Reference<T>> elements;

    /**
     * Sets the list.
     * 
     * @param list The list
     */
    public void set(List<T> list) {
        if (list.size() == 0) {
            clear();
            return;
        }
        this.list = new SoftReference<List<T>>(list);
        setElements(list);
    }

    /**
     * Populates {@link #elements} with the entries of the given list.
     * 
     * @param list The list
     */
    private void setElements(List<T> list) {
        elements = new ArrayList<Reference<T>>(list.size());
        for (T o : list)
            elements.add(new SoftReference<T>(o));
    }

    /**
     * Returns the list.<br>
     * This method returns a list holding all existing entries of the initially passed list.
     * 
     * @return The list
     */
    public List<T> get() {
        if (list == null)
            return Collections.emptyList();
        final List<T> l = list.get();
        if (l != null)
            return l;
        return getList();
    }

    /**
     * This method creates a new list holding all existing entries of {@link #elements} and sets the reference {@link #list} to the returned list.
     * 
     * @return The list
     */
    private List<T> getList() {
        final List<T> res = new ArrayList<T>();
        for (int i = 0; i < elements.size(); i++) {
            final T el = elements.get(i).get();
            if (el != null)
                res.add(el);
            else
                elements.remove(i);
        }
        // cache the list holding the surviving entries or clear the holder if there are no entries left
        if (res.size() > 0)
            list = new SoftReference<List<T>>(res);
        else
            clear();
        return res;
    }

    /**
     * Clears the content.
     */
    public void clear() {
        if (list == null)
            return;
        list = null;
        elements.clear();
        elements = null;
    }
}